#include "bsp_usart.h"

/*******************USART编程要点*********************************************/
//使能RX和TX引脚GPIO时钟和USART时钟     		开时钟
//初始化GPIO，并将GPIO复用到USART上
//配置USART参数
//配置中断控制器并使能中断
//使能USART
//在USART中断服务函数中写相应的功能
//      tx和rx都是相对于串口来讲的
uint8_t	usart_tx_fifo[256], usart_rx_fifo[256];
uint16_t tx_fifo_size=0,rx_fifo_size=0;
uint8_t Rxflagframe = 0; //串口接收到的数据可以发送了
/**
 *	@brief 嵌套向量中断控制器(NVIC)初始化函数(当有多个中断时，会怎样)
 *	@param 无
 *	@retval 无
 */
static	void	NVIC_Configuration(void)    //静态函数仅供本文件调用
{
    NVIC_InitTypeDef NVIC_InitStruct;     					//初始化NVIC结构体
    //最重要的是要分组
    NVIC_PriorityGroupConfig  (NVIC_PriorityGroup_2);       //配置优先级组  (各占两位)

    NVIC_InitStruct.NVIC_IRQChannel=DEBUG_USART_IRQ;    	//中断号
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;   //抢占优先级1级
    NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;          //子优先级
    NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;             //使能中断
    NVIC_Init  (&NVIC_InitStruct );                         //执行初始化
}

/**
 *	@brief USART初始化函数(串口接收得用中断:但这个中断源有好几个)
 *	@param 无
 *	@retval 无
 */
void	USART_Config(void)
{
    GPIO_InitTypeDef	GPIO_InitStruct;
    USART_InitTypeDef	USART_InitStruct;

    //开启相关外设的时钟
    DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK,ENABLE);    			//开启USART1的时钟
    DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK,ENABLE);   //开启GPIO的时钟

    //配置USART的相应管脚的模式
    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;          //Tx管脚
    GPIO_InitStruct.GPIO_Pin=DEBUG_USART_TX_GPIO_PIN;
    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init  (DEBUG_USART_TX_GPIO_PORT,&GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;          //Rx管脚
    GPIO_InitStruct.GPIO_Pin=DEBUG_USART_RX_GPIO_PIN;
    GPIO_Init  (DEBUG_USART_RX_GPIO_PORT,&GPIO_InitStruct);

    //配置串口的工作参数   即波特率=115200   数据长度=8bit    停止位=1bit   校验位=无  硬件流控制=无
    USART_InitStruct.USART_BaudRate=DEBUG_USART_BAUDRATE;
    USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;     //配置模式(收发一起)
    USART_InitStruct.USART_Parity=USART_Parity_No ;
    USART_InitStruct.USART_StopBits=USART_StopBits_1;
    USART_InitStruct.USART_WordLength=USART_WordLength_8b;

    USART_Init  (DEBUG_USARTx,&USART_InitStruct );                 //DEBUG_USARTx一个结构体指针

    //配置USART中断的优先级
    NVIC_Configuration();
    //选择具体的USART中断类型  (这个可靠吗???)
    USART_ITConfig(DEBUG_USARTx,USART_IT_IDLE,ENABLE);  //空闲中断 (有好几个中断,都可引起USART中断)
	  //开启串口DMA接收
	  USART_DMACmd(DEBUG_USARTx,USART_DMAReq_Rx,ENABLE);
    //使能USART
    USART_Cmd  (DEBUG_USARTx,ENABLE);
}

void USARTx_DMA_Config(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	/*开启DMA时钟*/
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	//一次要发送的数据个数
    DMA_InitStructure.DMA_BufferSize=RX_FIFO_SIZE;
    //配置数据传输的方向(目的地为SRAM)
    DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC ;
    //禁止内存到内存的传输
    DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
    //内存地址(要传输的变量的指针)
    DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)usart_rx_fifo;
    //串口只能一个Byte一个Byte的发送
    DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
    //内存地址自增
    DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
    //循环模式
    DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;
    //
    DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)USART1_DR_ADDRESS;
    //
    DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
    //
    DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
    //
    DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;
    //初始化
    DMA_Init  (USART1_RX_DMA_CHANNEL,&DMA_InitStructure) ;
	//清除DMA通道5的所用标志
	DMA_ClearFlag(DMA1_FLAG_GL5);
    //DMA_ITConfig(USART_RX_DMA_CHANNEL, DMA_IT_TE, ENABLE);	没什么屌用
    //使能DMA
    DMA_Cmd  (USART1_RX_DMA_CHANNEL,ENABLE);
}

/**
 *	@brief 该函数中断中调用
 */
void USARTx_DMA_RX_Data(void)
{
	//关闭DMA,防止干扰
    DMA_Cmd(USART1_RX_DMA_CHANNEL,DISABLE);
	rx_fifo_size=USART1_RX_DMA_CHANNEL->CNDTR;
	rx_fifo_size=RX_FIFO_SIZE-rx_fifo_size;
	//清除DMA通道5的所用标志
	DMA_ClearFlag(DMA1_FLAG_GL5);
	//重新赋值计数器，必须大于等于最大可能接收到的数据帧数目
	USART1_RX_DMA_CHANNEL->CNDTR=RX_FIFO_SIZE;  //重新开始接收
    //使能DMA
    DMA_Cmd  (USART1_RX_DMA_CHANNEL,ENABLE);
	//置位相关标志
	Rxflagframe=1;
	
	/*
		DMA开启，等待数据。注意，如果中断发送数据帧的速率很快，MCU来不及处理此次接收到的数据，
		中断又发来数据的话，这里不能开启，否则数据会被覆盖。有2种解决方式
			1.在重新开启接收DMA通道之前，将LumMod_Rx_Buf缓冲区里面的数据复制到另外一个数组中，
			  然后再开启DMA,然后马上处理复制出来的数据
			
			2.建立双缓冲,在LumMod_Uart_DMA_Rx_Data函数中，重新配置DMA_MemoryBaseAddr的缓冲区地址,
			  那么下次接收到的数据就会保存到新的缓冲区中去，不至于被覆盖.
	*/
}

//发送一个字符
void	USART_SendByte(USART_TypeDef *  USARTx,uint8_t  ch)
{
    USART_SendData  (USARTx,ch);
    //下面就是等待发送完成       //TXE发送寄存器为空(但移位寄存器可能并不为空)，发送单个字节的时候使用
    while(USART_GetFlagStatus  (USARTx,USART_FLAG_TXE)==RESET);
}

/**
 *	@brief :将无线模块接收到的数据通过串口发出
 *	@param :无
 *	@retval:无
 */
void USARTSendData(void)
{
   uint16_t i=0;

    while(tx_fifo_size--)
    {
        USART_SendByte(DEBUG_USARTx, usart_tx_fifo[i++]);
    }
	tx_fifo_size=0;      //very importment
}

//下面得重写一些标准的C库函数(需要重写前面包含stdio.h吗)

//重定向c库函数printf到串口，重定向后可使用printf函数
int	fputc(int ch,FILE *f)    //这里的FILE *f其实没什么屌用
{
    USART_SendByte(DEBUG_USARTx,(uint8_t)ch);
    while(USART_GetFlagStatus  (DEBUG_USARTx,USART_FLAG_TXE)==RESET);  //等待发送完成
    return	ch;   //按标准返回
}

//重定向c库函数scanf到串口,重定向后可使用scanf,getchar等函数
int	fgetc(FILE	*f)
{
    while(USART_GetFlagStatus  (DEBUG_USARTx,USART_FLAG_RXNE)==RESET);
    return USART_ReceiveData  (DEBUG_USARTx);
}





